/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (c) 2012, Igor Kozhukhov <ikozhukhov@gmail.com>.  All rights reserved.
 */

/*
 * This implements lgrpinfo(1M), display information about locality groups.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <errno.h>
#include <libgen.h>
#include <ctype.h>
#include <unistd.h>
#include <libintl.h>
#include <kstat.h>
#include <libproc.h>
#include <sys/lgrp_user.h>

#define	_(x)			gettext(x)
#define KB			(1024LL)

#define LGRP_LOADAVG_THREAD_MAX	65516

#define	NUMBER_WIDTH		64

#define LGRP_NAME_ROOT		"root"
#define LGRP_NAME_CHILDREN	"children"
#define LGRP_NAME_LEAF		"leaf"
#define LGRP_NAME_PARENT	"parent"


#ifndef	TEXT_DOMAIN			/* should be defined by cc -D */
#define	TEXT_DOMAIN	"SYS_TEST"	/* use this only if it wasn't */
#endif

/* structure of 'lgrp' data */
typedef struct lgrps {
	int id;
	lgrp_id_t lgrp;
	char *name;
	char *loadavg;
	char *children;
	char *cpus;
	char *memstr;
	char *rsrc;
	int latency;
	struct lgrps *next;
	struct lgrps *prev;
} lgrps_t;

/* structure CPUs info */
typedef struct cpus {
	int count;
	int *cpus;
}cpus_t;


/*
 * Name of this program
 */
static const char *cmdname;


/*
 * usage(exit_status)
 * print usage message and exit with the specified exit status.
 */
static void
usage(int ret)
{
	(void) fprintf(stderr, _("Usage:\t%s"), cmdname);
	(void) fprintf(stderr, " [-aceGlLmrt] [-u unit] [-C|-P] [lgrp] ...\n");
	(void) fprintf(stderr, "      \t%s -I [-c] [-G] [-C|-P] [lgrp] ...\n", 
					cmdname);
	(void) fprintf(stderr, "      \t%s -T [-aceGlLmr] [-u unit]\n", cmdname);
	(void) fprintf(stderr, "      \t%s -h\n\n", cmdname);

	(void) fprintf(stderr,
	  _("   Display information about locality groups\n\n" \
		  "\t-a: Equivalent to \"%s\" without -T and to \"%s\" with -T\n"),
		    "-celLmrt", "-celLmr");

	(void) fprintf(stderr,
	  _("\t-c: Print CPU information\n" \
	  "\t-C: Children of the specified lgroups\n" \
	  "\t-e: Print lgroup load average\n" \
	  "\t-h: Print this message and exit\n" \
	  "\t-I: Print lgroup or CPU IDs only\n" \
	  "\t-l: Print information about lgroup latencies\n" \
	  "\t-G: Print OS view of lgroup hierarchy\n" \
	  "\t-L: Print lgroup latency table\n" \
	  "\t-m: Print memory information\n" \
	  "\t-P: Parent(s) of the specified lgroups\n" \
	  "\t-r: Print lgroup resources\n" \
	  "\t-t: Print information about lgroup topology\n" \
	  "\t-T: Print the hierarchy tree\n" \
	  "\t-u unit: Specify memory unit (b,k,m,g,t,p,e)\n\n\n"));

	(void) fprintf(stderr,
	  _("    The lgrp may be specified as an lgroup ID," \
	  " \"root\", \"all\",\n" \
	  "    \"intermediate\" or \"leaves\".\n\n"));

	(void) fprintf(stderr,
	  _("    The default set of options is \"%s\"\n\n"),
	    "-celmrt all");

	(void) fprintf(stderr,
	  _("    Without any options print topology, CPU and memory " \
		  "information about each\n" \
		  "    lgroup. If any lgroup IDs are specified on the " \
		  "command line only print\n" \
		  "    information about the specified lgroup.\n\n"));

	exit(ret);
}


/* like perror, but includes the command name */
static void
die(const char *msg)
{
	(void) fprintf(stderr, "%s: %s: %s\n", cmdname, msg, strerror(errno));
	exit(2);
}

static char *
mystrdup(const char *src)
{
	char *dst;

	if ((dst = strdup(src)) == NULL)
		die(_("strdup() failed"));
	return (dst);
}

static void
nsort(int *data, int count)
{
	int i;
	int temp;
	int sort = 1;
	
	while (sort) {
		for (i = 1; i < count; i++) {
			if (data[i] < data[i - 1]) {
				temp = data[i];
				data[i] = data[i - 1];
				data[i - 1] = temp;
				sort = 1;
				break;
			} else {
				sort = 0;
			}
		}
	}
}


static char *
lgrp_collapse(int *args, int cnt)
{
	int start;
	char *result;
	char buff[80];
	int i;
	int el;
	int end;

	start = args[0];
	end = start;	/* Initial range consists of the first element */

	if (cnt > 1)
		(void) nsort(args, cnt);

	for (i = 0; i < cnt; i++) {
		el = args[i];

		if (el == (end + 1)) {
			/*
			* Got consecutive ID, so extend end of range without
			* printing anything since the range may extend further
			*/
			end = el;
		} else {
			/* Try finding consecutive range starting from this ID */
			start = end = el;
		}
	}

	/* Print last ID(s) */
	if (end > start + 1) {
		sprintf(buff, "%d-%d", start, end);
	} else if (end > start) {
		sprintf(buff, "%d %d", start, end);
	} else {
		sprintf(buff, "%d", start);
	}

	result = mystrdup(buff);

	return (result);
}


static cpus_t *
get_cpus(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_content_t content)
{
	cpus_t *cpust = NULL;
	int ncpus;
	processorid_t *cpus = NULL;
	int i;

	ncpus = lgrp_cpus(cookie, lgrp, NULL, 0, content);
	if (ncpus < 0)
		die("Error reading number of cpu's");

	if ((cpus = calloc(ncpus, sizeof(processorid_t))) == NULL)
		die("no memory!");

	ncpus = lgrp_cpus(cookie, lgrp, cpus, ncpus, content);

	if (ncpus > 0) {
		if ((cpust = calloc(ncpus, sizeof(cpus_t))) == NULL)
			die("no memory!");

		if ((cpust->cpus = calloc(ncpus, sizeof(int))) == NULL)
			die("no memory!");

		cpust->count = ncpus;
		for (i = 0; i < ncpus; i++) {
			cpust->cpus[i] = cpus[i];
		}
	}

	return (cpust);
}


/* What CPUs are in this lgrp? */
static char *
lgrp_showcpus(lgrp_cookie_t cookie, lgrp_id_t lgrp, lgrp_content_t content)
{
	cpus_t *cpus;
	char buff[80];
	char *str;

	cpus = get_cpus(cookie, lgrp, content);

	str = lgrp_collapse(cpus->cpus, cpus->count);

	if (cpus->count == 1)
		sprintf(buff, "CPU: %s", str);
	else
		sprintf(buff, "CPUs: %s", str);

	str = mystrdup(buff);

	return str;
}


/*
 * Convert a number to a string representation
 * The number is scaled down until it is small enough to be in a good
 * human readable format i.e. in the range 0 thru 1023.
 * If it's smaller than 10 there's room enough to provide one decimal place.
 */
static char *
number_to_scaled_string(lgrp_mem_size_t number)
{
	lgrp_mem_size_t scale = KB;

	lgrp_mem_size_t save = 0L;
	char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
	char *uom = M;    /* unit of measurement, initially 'K' (=M[0]) */

	char *result;

	save = number;

	if((result = calloc(NUMBER_WIDTH, sizeof(char))) == NULL)
		die("No memory");

	/* Get size in K. */
	number /= KB;

	while ((number >= scale) && (*uom != 'E')) {
		uom++; /* next unit of measurement */
		save = number;
		number = (number + (scale / 2)) / scale;
	}

	/* check if we should output a decimal place after the point */
	if (save && ((save / scale) < 10)) {
		float fnum = (float)save / scale;
		(void) sprintf(result, "%2.1f%c", fnum, *uom);
	} else {
		(void) sprintf(result, "%2llu%c", number, *uom);
	}
	return (result);
}


/* Convert memory size to the string representation */
static char *
memory_to_string(lgrp_mem_size_t number, char *opt_u)
{
	float scaled;
	lgrp_mem_size_t units;
	char unit_str;
	char *result;
	char buff[NUMBER_WIDTH];

	units = KB;
	unit_str = 'K';

	/* Zero memory - just print 0 */
	if (number == 0) {
		sprintf(buff, "0%c", unit_str);
		result = mystrdup(buff);
		return (result);
	}
	
	/*
	 * Return memory size scaled to human-readable form unless -u is
	 * specified.
	 */
	if (opt_u == NULL)
		return (number_to_scaled_string(number));

	/* Convert units to canonical numeric and string formats. */
	if (opt_u) {
		if (strcmp(opt_u, "b") == 0 || strcmp(opt_u, "B") == 0) {
			units = 1L;
			unit_str = 'B';
		} else if (strcmp(opt_u, "k") == 0 || strcmp(opt_u, "K") == 0) {
			units = KB;
			unit_str = 'K';
		} else if (strcmp(opt_u, "m") == 0 || strcmp(opt_u, "M") == 0) {
			units = (KB * KB);
			unit_str = 'M';
		} else if (strcmp(opt_u, "g") == 0 || strcmp(opt_u, "G") == 0) {
			units = (KB * KB * KB);
			unit_str = ('G');
		} else if (strcmp(opt_u, "t") == 0 || strcmp(opt_u, "T") == 0) {
			units = (KB * KB * KB * KB);
			unit_str = 'T';
		} else if (strcmp(opt_u, "p") == 0 || strcmp(opt_u, "P") == 0) {
			units = (KB * KB * KB * KB * KB);
			unit_str = 'P';
		} else if (strcmp(opt_u, "e") == 0 || strcmp(opt_u, "E") == 0) {
			units = (KB * KB * KB * KB * KB * KB);
			unit_str = 'E';
		} else if (! (strcmp(opt_u, "m") == 0 || strcmp(opt_u, "M") == 0)) {
			(void) fprintf(stderr,
				_("%s: invalid unit '%s', should be [b|k|m|g|t|p|e]"),
				cmdname, opt_u);
			(void) fprintf(stderr, _(", using the default.\n\n"));
		}
	}

	scaled = (float)number / units;

	if (scaled < 0.01) {
		(void) sprintf(buff, "%f%c", scaled, unit_str);
	} else if (scaled < 0.1) {
		(void) sprintf(buff, "%2.2f%c", scaled, unit_str);
	} else if (scaled < 10) {
		(void) sprintf(buff, "%2.1f%c", scaled, unit_str);
	} else {
		(void) sprintf(buff, "%d%c", (int)(scaled + 0.5), unit_str);
	}
	result = mystrdup(buff);
	return (result);
}


/* How much memory does this lgrp contain? */
static char *
lgrp_showmemory(lgrp_cookie_t cookie, lgrp_id_t lgrp, 
	lgrp_content_t content, char *opt_u)
{
	lgrp_mem_size_t memory;
	lgrp_mem_size_t freemem;
	char *memstr;
	char *buff;
	char *memory_r, *freemem_r, *usedmem;

	memory = lgrp_mem_size(cookie, lgrp, LGRP_MEM_SZ_INSTALLED, content);
	freemem = lgrp_mem_size(cookie, lgrp, LGRP_MEM_SZ_FREE, content);

	memory_r = memory_to_string(memory, opt_u);
	freemem_r = memory_to_string(freemem, opt_u);
	usedmem = memory_to_string(memory - freemem, opt_u);

	if((memstr = calloc(strlen("Memory: installed ") + strlen(memory_r) + 1, 
			sizeof(char))) == NULL)
		die("No memory");

	sprintf(memstr, _("Memory: installed %s"), memory_r);
	buff = mystrdup(memstr);
	free(memstr);

	if((memstr = calloc(strlen(buff) + strlen(", allocated ") + 
						strlen(usedmem) + 1,
						sizeof(char))) == NULL)
		die("No memory");
	
	sprintf(memstr, _("%s, allocated %s"), buff, usedmem);
	free(buff);
	buff = mystrdup(memstr);
	free(memstr);

	if((memstr = calloc(strlen(buff) + strlen(", free ") + strlen(freemem_r) + 1,
			sizeof(char))) == NULL)
		die("No memory");

	sprintf(memstr, _("%s, free %s"), buff, freemem_r);
	free(buff);

	return (memstr);
}


/* Get string containing lgroup resources */
static char *
lgrp_showresources(lgrp_cookie_t cookie, lgrp_id_t lgrp)
{
	int resources_cpu;
	int resources_mem;
	char buff[80];
	char *rsrc;
	char *rsrc_cpu;
	char *rsrc_mem;
	lgrp_id_t *lgrps;

	/* What resources does this lgroup contain? */
	resources_cpu = lgrp_resources(cookie, lgrp, NULL, 0, LGRP_RSRC_CPU);
	if ((lgrps = calloc(resources_cpu, sizeof(lgrp_id_t))) == NULL)
		die("no memory!");
	resources_cpu = lgrp_resources(cookie, lgrp, lgrps, resources_cpu, 
		LGRP_RSRC_CPU);

	rsrc_cpu = lgrp_collapse((int *)lgrps, resources_cpu);
	free(lgrps);

	resources_mem = lgrp_resources(cookie, lgrp, NULL, 0, LGRP_RSRC_MEM);
	if ((lgrps = calloc(resources_mem, sizeof(lgrp_id_t))) == NULL)
		die("no memory!");
	resources_mem = lgrp_resources(cookie, lgrp, lgrps, resources_mem, 
		LGRP_RSRC_MEM);

	rsrc_mem = lgrp_collapse((int *)lgrps, resources_mem);
	free(lgrps);

	(void) sprintf(buff, _("Lgroup resources: %s (CPU), %s (memory)"), 
				rsrc_cpu, rsrc_mem);

	rsrc = mystrdup(buff);

	return (rsrc);
}


static void
set_lav(lgrps_t *lgrps, lgrp_id_t lgrp, const char *str)
{
	lgrps_t *llgrps;

	for (llgrps = lgrps;llgrps; llgrps = llgrps->next) {
		if (llgrps->lgrp == lgrp)
			llgrps->loadavg = mystrdup(str);
	}
	lgrps = llgrps;
}

/*
 * Read load averages from lgrp kstats Return hash reference indexed by lgroup ID
 * for each lgroup which has load information.
 */
static void
get_lav(lgrps_t *lgrps)
{
	kstat_ctl_t *kc;
	kstat_t 	*ksp;
	kstat_named_t	*knp;
	ulong_t	lav = 0L, scale = 0L;
	float		fnum;

	char		buff[80];

	/* collect the kstats */
	if ((kc = kstat_open()) == NULL)
		die(_("kstat_open() failed"));

	if ((ksp = kstat_lookup(kc, "lgrp", -1, NULL)) == NULL)
		die(_("kstat_lookup() failed"));

	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {

		if (strcmp(ksp->ks_module, "lgrp") != 0)
			continue;
		
		if (kstat_read(kc, ksp, NULL) == (kid_t)NULL)
			die(_("kstat_read() failed"));

		if ((knp = kstat_data_lookup(ksp, "load average")) != NULL)
			lav = knp->value.ui64;

		if ((knp = kstat_data_lookup(ksp, "loadscale")) != NULL)
			scale = knp->value.ui64;

		if (scale <= 0L)
			scale = LGRP_LOADAVG_THREAD_MAX;

		fnum = (float)lav / scale;

		(void) sprintf(buff, _("Load: %4.3f"), fnum);

		(void) set_lav(lgrps, (lgrp_id_t)ksp->ks_instance, buff);
	}

	(void) kstat_close(kc);
}


/* Print latency information if requested and the system has several lgroups. */
static void
print_latency_table(lgrp_cookie_t cookie, lgrps_t *lgrps1)
{
	lgrps_t *llgrps;
	lgrp_id_t *lgrps;
	lgrp_id_t root;
	int cnt = 0;
	int counts;
	lgrp_id_t max;
	int latency;
	char buff[80];
	int lgwidth;
	int width;
	int i, j;

	root = lgrp_root(cookie);

	for (llgrps = lgrps1; llgrps; llgrps = llgrps->next)
		cnt++;

	counts = cnt;

	if ((lgrps = calloc(counts, sizeof(lgrp_id_t))) == NULL)
		die("No memory!");

	/* Find maximum lgroup */
	max = root;
	cnt = 0;
	for (llgrps = lgrps1; llgrps; llgrps = llgrps->next) {
		if (max < llgrps->lgrp)
			max = llgrps->lgrp;
		lgrps[cnt++] = llgrps->lgrp;
	}

	(void) sprintf(buff, "%d", max);

	/* Field width for lgroup - the width of the largest lgroup and 1 space */
	lgwidth = strlen(buff) + 1;

	/* Field width for latency. Get the maximum latency and add 1 space. */
	latency = lgrp_latency_cookie(cookie, root, root, 0);
	(void) sprintf(buff, "%d", latency);
	width = strlen(buff) + 1;

	/* Make sure that width is enough to print lgroup itself. */
	if (width < lgwidth)
		width = lgwidth;

	/* Print table header */
	(void) printf(_("\nLgroup latencies:\n"));

	/* Print horizontal line */
	(void) printf("\n");
	for (i = 0; i < (lgwidth + width * counts + 1); i++)
		(void) printf("-");

	(void) printf("\n%*c|", lgwidth, ' ');

	for (i = 0; i < counts; i++) {
		(void) printf("%*i", width, lgrps[i]);
	}

	/* Print horizontal line */
	(void) printf("\n");
	for (i = 0; i < (lgwidth + width * counts + 1); i++)
		(void) printf("-");

	for (i = 0; i < counts; i++) {
		(void) printf("\n%*i|", lgwidth, lgrps[i]);
		for (j = 0; j < counts; j++) {
			latency = lgrp_latency_cookie(cookie, lgrps[i], lgrps[j], 0);
			(void) printf("%*i", width, latency);
		}
	}

	/* Print table footer */
	(void) printf("\n");
	for (i = 0; i < (lgwidth + width * counts + 1); i++)
		(void) printf("-");

	printf("\n");
}

static char *
get_children(lgrp_cookie_t cookie, lgrp_id_t lgrp)
{
	lgrp_id_t *lgrps, *lgrpp;
	int count;
	int prnt;
	char buff[80];
	char *result = NULL;

	if ((count = lgrp_children(cookie, lgrp, NULL, 0)) < 0)
		die("Error get count of children");

	if (count > 0) {

		if ((lgrps = calloc(count, sizeof(lgrp_id_t))) == NULL)
			die("No memory");
		count = lgrp_children(cookie, lgrp, lgrps, count);

		result = lgrp_collapse((int *)lgrps, count);
		(void) sprintf(buff, "Children: %s", result);

		result = mystrdup(buff);
	}
	else {
		(void) sprintf (buff, "Children: none");

		if ((prnt = lgrp_parents(cookie, lgrp, NULL, 0)) < 0)
			die("Error get count of parent");
		
		if (prnt > 0) {
			if ((lgrpp = calloc(prnt, sizeof(lgrp_id_t))) == NULL)
				die("No memory");
			prnt = lgrp_parents(cookie, lgrp, lgrpp, prnt);
			if (prnt > 0) {
				result = mystrdup(buff);
				(void) sprintf(buff, "%s, Parent: %u", result, *lgrpp);
				free(result);
			}
		}
		result = mystrdup(buff);
	}

	return result;
}


static lgrps_t *
lgrp_lgrps(lgrp_cookie_t cookie, char *opt_u, const char *param)
{
	lgrps_t *lgrps, *save, *first;
	lgrp_id_t root;
	int count;
	int lcnt = 0;
	int i;
	char *str;
	lgrp_id_t *lgrp;
	int c_all = 0;
	int c_intermediate = 0;
	int c_root = 0;
	int c_leaves = 0;
	char lbuff[NUMBER_WIDTH];
	int found;

	if (param == NULL)
		c_all = 1;

	if (param != NULL && strcmp(param, "all") == 0)
		c_all = 1;

	if (param != NULL && strcmp(param, "root") == 0)
		c_root = 1;

	if (param != NULL && strcmp(param, "0") == 0)
		c_root = 1;

	if (param != NULL && strcmp(param, "intermediate") == 0)
		c_intermediate = 1;

	if (param != NULL && strcmp(param, "leaves") == 0)
		c_leaves = 1;

	root = lgrp_root(cookie);
	if (root == -1)
		die("Error with getting 'root' lgrp");

	count = lgrp_nlgrps(cookie);
	if (count == -1)
		die("Error with getting nlgrps");

	if ((lgrps = calloc(1, sizeof(lgrps_t))) == NULL)
		die("No memory");
	first = lgrps;

	if (c_all || c_root) {
		lgrps->id = lcnt;
		lgrps->prev = NULL;
		lgrps->lgrp = root;
		lgrps->name = mystrdup(LGRP_NAME_ROOT);
		str = get_children(cookie, lgrps->lgrp);
		lgrps->children = mystrdup(str);
		str = lgrp_showcpus(cookie, lgrps->lgrp, LGRP_CONTENT_HIERARCHY);
		lgrps->cpus = mystrdup(str);
		str = lgrp_showmemory(cookie, lgrps->lgrp, LGRP_CONTENT_HIERARCHY, 
						opt_u);
		lgrps->memstr = mystrdup(str);
		str = lgrp_showresources(cookie, lgrps->lgrp);
		lgrps->rsrc = mystrdup(str);
		lgrps->latency = lgrp_latency_cookie(cookie, lgrps->lgrp, lgrps->lgrp, 
						0);

		if (c_root)
			return lgrps;

		lcnt++;
	}

	if ((count = lgrp_children(cookie, root, NULL, 0)) < 0)
		die("Error get count of children");

	if (count > 0) {
		if ((lgrp = calloc(count, sizeof(lgrp_id_t))) == NULL)
			die("No memory");
		count = lgrp_children(cookie, root, lgrp, count);

		for (i = 0; i < count; i++) {

			if ((!c_root && i > 0) || c_all) {
				if ((lgrps->next = calloc(1, sizeof(lgrps_t))) == NULL)
					die("No memory");
				save = lgrps;
				lgrps = lgrps->next;
				lgrps->prev = save;
			}
			lgrps->lgrp = lgrp[i];
			lgrps->id = lcnt;

			if ((lgrp_children(cookie, lgrps->lgrp, NULL, 0)) > 0)
				lgrps->name = mystrdup(LGRP_NAME_CHILDREN);
			else
				lgrps->name = mystrdup(LGRP_NAME_LEAF);

			str = get_children(cookie, lgrps->lgrp);
			lgrps->children = mystrdup(str);

			str = lgrp_showcpus(cookie, lgrps->lgrp, LGRP_CONTENT_HIERARCHY);
			lgrps->cpus = mystrdup(str);
			str = lgrp_showmemory(cookie, lgrps->lgrp, LGRP_CONTENT_HIERARCHY, 
									opt_u);
			lgrps->memstr = mystrdup(str);
			str = lgrp_showresources(cookie, lgrps->lgrp);
			lgrps->rsrc = mystrdup(str);
			lgrps->latency = lgrp_latency_cookie(cookie, lgrps->lgrp, 
												lgrps->lgrp, 0);

			lcnt++;
		}
	}

	if (!c_all && !c_intermediate && !c_leaves) {
		for(lgrps = first; lgrps; lgrps = lgrps->next) {
			(void) sprintf(lbuff, "%u", lgrps->lgrp);
			if (strcmp(param, lbuff) == 0) {
				first = lgrps;
				first->next = NULL;
				first->prev = NULL;
				found = 1;
				break;
			}
			found = 0;
		}
		if (!found) {
			(void) printf("%s: skipping invalid lgrp %s\n", cmdname, param);
			count = 0;
			c_intermediate = 1;
		}
	}

	if (count < 1 && c_intermediate)
		die("No matching lgroups found!");

	lgrps = first;

	return lgrps;
}


void
lgrp_print(lgrps_t *lgrp, char *prefix, int do_lat, int do_cpu, int do_memory, 
			int do_rsrc)
{
	int not_root = 1;

	if (strcmp(lgrp->name, LGRP_NAME_ROOT) == 0)
		not_root = 0;

	(void) printf("%u", lgrp->lgrp);

	/* Print all the information about lgrp. */

	if (do_cpu)
		(void) printf("\n%s%s", prefix, lgrp->cpus);

	if (do_memory)
		(void) printf("\n%s%s", prefix, lgrp->memstr);

	if (do_rsrc)
		(void) printf("\n%s%s", prefix, lgrp->rsrc);

	if (lgrp->loadavg)
		(void) printf("\n%s%s", prefix, lgrp->loadavg);
	
	/* Print latency information if requested. */
	if (do_lat && not_root && lgrp->latency) {
		(void) printf("\n%s", prefix);
		(void) printf(_("Latency: %d"), lgrp->latency);
	}

	(void) printf("\n");
}


/* Pretty print the whole tree */
void
lgrp_prettyprint(lgrps_t *lgrps, int do_lat, int do_cpu, int do_memory, 
			int do_rsrc)
{
	lgrps_t *llgrps;
	int cnt = 0;
	int npeers;
	char bar;
	char *printpostfix;

	for (llgrps = lgrps; llgrps; llgrps = llgrps->next)
		cnt++;

	npeers = cnt;
	if (cnt > 1)
		npeers = cnt - 1;

	for (llgrps = lgrps; llgrps; llgrps = llgrps->next) {

		if (strcmp(llgrps->name, LGRP_NAME_ROOT) == 0 && (cnt > 1)) {
			(void) printf("%u\n", llgrps->lgrp);
			npeers--;
			continue;
		}

		printpostfix = npeers ? mystrdup("|   ") : mystrdup("    ");

		bar = npeers ? '|' : '`';

		if (strcmp(llgrps->name, LGRP_NAME_ROOT) != 0) {
			(void) printf("%c-- ", bar);
		}

		(void) lgrp_print(llgrps, printpostfix, do_lat, do_cpu, do_memory, 
						do_rsrc);

		npeers--;
	}
}


int
main(int argc, char *argv[])
{
	int		ret = 0;

	int		optc;
	int		do_default = 0;
	int		opt_a = 0,
			opt_c = 0,
			opt_e = 0,
			opt_G = 0,
			opt_h = 0,
			opt_l = 0,
			opt_L = 0,
			opt_I = 0,
			opt_m = 0,
			opt_r = 0,
			opt_t = 0,
			opt_T = 0,
#if defined(__GNUC__)
			opt_C __attribute__((__unused__)),
			opt_P __attribute__((__unused__));
			opt_C = 0;
			opt_P = 0;
#else
			opt_C = 0,
			opt_P = 0;
#endif

	int		do_all = 0,
			do_lat = 0,
			do_memory = 0,
			do_cpu = 0,
			do_topo = 0,
			do_rsrc = 0,
			do_load = 0,
			do_table = 0,
			do_something = 0;

	char		*opt_u = NULL;
	int		nfilters = 0;
	lgrp_view_t view;
	lgrp_cookie_t cookie;
	
	lgrps_t *lgrps, *save;
	cpus_t 	*cpus;
	int 	i;

	cmdname = basename(argv[0]);

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	while ((optc = getopt(argc, argv, "acCeGhlLImrtTPu:u:")) != EOF) {

		switch (optc) {

		case 'a':
			opt_a = 1;
			break;

		case 'c':
			opt_c = 1;
			break;

		case 'C':
			opt_C = 1;
			nfilters++;
			break;

		case 'e':
			opt_e = 1;
			break;

		case 'G':
			opt_G = 1;
			break;

		case 'h':
			opt_h = 1;
			break;

		case '?':
			opt_h = 1;
			break;

		case 'l':
			opt_l = 1;
			break;

		case 'L':
			opt_L = 1;
			break;

		case 'I':
			opt_I = 1;
			break;

		case 'm':
			opt_m = 1;
			break;

		case 'r':
			opt_r = 1;
			break;

		case 't':
			opt_t = 1;
			break;

		case 'T':
			opt_T = 1;
			nfilters++;
			break;

		case 'u':
			opt_u = optarg;
			break;

		case 'P':
			opt_P = 1;
			nfilters++;
			break;

		default:
			usage(0);
			break;
		}
	}

	if (opt_h)
		usage(0);

	if (nfilters > 1) {
		(void) fprintf(stderr,
		_("%s: Options -C, -T and -P can not be used together\n"),
			cmdname);
		usage(3);
	}

	if (opt_T && (opt_I || opt_t)) {
		(void) fprintf(stderr,
		_("%s: Option -T can not be used with -I, -t\n"),
			cmdname);
		usage(3);
	}

	if (opt_T && argc > optind) {
		(void) fprintf(stderr,
		_("%s: Warning: with '-T' all lgroups on the command line "),
			cmdname);
		(void) fprintf(stderr,
		_("are ignored\n\n"));
		strcpy(argv[optind], "all");
	}

	if (opt_L && opt_I) {
		(void) fprintf(stderr,
		_("%s: Option -I can not be used with -L\n"),
			cmdname);
		usage(3);
	}

	/* Figure out what to do based on options */
	if (!opt_a && !opt_l && !opt_m && !opt_c && !opt_e && !opt_t && !opt_r)
		do_default = 1;

	view = opt_G ? LGRP_VIEW_OS : LGRP_VIEW_CALLER;
	if ((cookie = lgrp_init(view)) == LGRP_COOKIE_NONE)
		die(_("can not get lgroup information from the system"));

	lgrps = lgrp_lgrps(cookie, opt_u, argv[optind]);
	
	save = lgrps;

	/* Print everything if -a is specified or it is default without -T */
	if(opt_a || (do_default && !(opt_T || opt_L)))
		do_all = 1;

	/* Print individual information if do_all or requested specific print */
	if(do_all || opt_l)
		do_lat = 1;

	if(do_all || opt_m)
		do_memory = 1;

	if(do_all || opt_c)
		do_cpu = 1;

	if(do_all || opt_t)
		do_topo = 1;

	if(do_all || opt_r)
		do_rsrc = 1 ;

	if(do_all || opt_e)
		do_load = 1;

	if(opt_a  || opt_L)
		do_table = 1;
	
	do_something = (do_lat || do_memory || do_cpu || do_topo ||
			do_rsrc || do_load);


	/* Does the liblgrp(3LIB) has enough capabilities to support resource view? */
	if (do_rsrc && LGRP_VER_CURRENT == 1) {
		if (opt_r) {
			(void) fprintf(stderr,
			_("%s: sorry, your system does not support"
			" lgrp_resources(3LGRP)\n"), cmdname);
		}
		do_rsrc = 0;
	}

	/* If both -I and -c are specified, just print list of CPUs. */
	if (opt_c && opt_I) {
		cpus = get_cpus(cookie, lgrps->lgrp, LGRP_CONTENT_HIERARCHY);

		for (i = 0; i < cpus->count; i++) {
			(void) printf("%d ", cpus->cpus[i]);
		}
		(void) printf("\n");

		(void) lgrp_fini(cookie);
		exit(0);
	}

	/* Collect load average data if requested. */
	if (do_load)
		(void) get_lav(lgrps);

	/* If -T is specified, just print topology and return. */
	if (opt_T) {
		(void) lgrp_prettyprint(lgrps, do_lat, do_cpu, do_memory, do_rsrc);

		if (do_table)
			(void) print_latency_table(cookie, lgrps);

		(void) lgrp_fini(cookie);
		exit(0);
	}

	/* Just print list of lgrps if doing just filtering */
	if (opt_I) {
		for (lgrps = save; lgrps; lgrps = lgrps->next) {
			(void) printf("%u ", lgrps->lgrp);
		}
		(void) printf("\n");

		(void) lgrp_fini(cookie);
		exit (0);
	}

	if (do_something) {

		for (lgrps = save; lgrps; lgrps = lgrps->next) {

			(void) printf(_("lgroup %d (%s):"), lgrps->lgrp, lgrps->name);

			/* Print all the information about lgrp. */
			if (do_topo)
				(void) printf("\n\t%s", lgrps->children);

			if (do_cpu)
				(void) printf("\n\t%s", lgrps->cpus);

			if (do_memory)
				(void) printf("\n\t%s", lgrps->memstr);

			if (do_rsrc)
				(void) printf("\n\t%s", lgrps->rsrc);

			if (lgrps->loadavg)
				(void) printf(_("\n\t%s"), lgrps->loadavg);

			if (do_lat)
				(void) printf("\n\tLatency: %d", lgrps->latency);

			(void) printf("\n");
		}
	}

	lgrps = save;

	if (do_table)
		(void) print_latency_table(cookie, lgrps);

	(void) lgrp_fini(cookie);

	return (ret);
}

